#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
#include <math.h>
#include <string.h>
#include "report.h"

/*****************************************************************************/

void Report_Real_Init(Report_Real_t * stat)
{
  stat->cnt = 0;
  stat->null_cnt = 0;
  stat->sum = 0;
  stat->sum_of_sq = 0;

  stat->last_is_null = 1;
}

void Report_Real_Update(Report_Real_t * stat, double val)
{
  stat->cnt ++;
  stat->sum += val;
  stat->sum_of_sq += val * val;

  stat->last_val = val;
  stat->last_is_null = 0;
}

void Report_Real_Update_Null(Report_Real_t * stat)
{
  stat->cnt ++;
  stat->null_cnt ++;

  stat->last_is_null = 1;
}


void Report_Real_Compute(const Report_Real_t * stat, double * pavg,
                         double * psd)
{
  if (stat->cnt == stat->null_cnt)
  {
    *pavg = 0;
    *psd = 0;
  }
  else
  {
    *pavg = stat->sum / ((double)stat->cnt - stat->null_cnt);
    *psd = sqrt((stat->sum_of_sq /(stat->cnt - stat->null_cnt))
                - *pavg * *pavg);
  }
}

void Report_Real_Print(const Report_Real_t * stat)
{
  double avg, sd;

  Report_Real_Compute(stat, &avg, &sd);
  
  printf("P(null) %.3g Avg. %.3g S.D. %.3g\n", 
         ((double)stat->null_cnt)/stat->cnt, avg, sd);
}

void Report_Real_Print_Last(const Report_Real_t * stat)
{
  if (stat->last_is_null)
    printf("P(null) 1 Value 0\n");

  else
    printf("P(null) 0 Value %.3g\n", stat->last_val);
}

/*****************************************************************************/
/* NaturalNum */
void Report_NaturalNum_Init(Report_NaturalNum_t * stat)
{
  stat->cnt = stat->null_cnt = 0;
  
  stat->num_val = 100;
  
  /* allocate and clear the memory for the counters */
  stat->p_valcnt = (long *)calloc(stat->num_val, sizeof(*stat->p_valcnt));

  if (!stat->p_valcnt)
  {
    fprintf(stderr, "fatal: unable to allocate '%d' bytes for "
            "Report_NaturalNum", stat->num_val * sizeof(*stat->p_valcnt));
    exit(1);
  }

  stat->last_is_null = 1;
}

void Report_NaturalNum_Update(Report_NaturalNum_t * stat, int val)
{
  stat->cnt ++;
  
  /* extend the size of the array */
  if (val >= stat->num_val)
  {
    int  old_num_val;
    long * old_p_valcnt;
    
    old_num_val = stat->num_val;
    old_p_valcnt = stat->p_valcnt;
    
    stat->num_val = 2 * val;                 /* increase the size */
    stat->p_valcnt = (long *)malloc(stat->num_val * sizeof(*stat->p_valcnt));

    if (!stat->p_valcnt)
    {
      fprintf(stderr, "fatal: unable to allocate '%d' bytes for "
              "Report_NaturalNum", stat->num_val * sizeof(*stat->p_valcnt));
      exit(1);
    }

    /* clear out the extra allocated memory */
    memset(stat->p_valcnt + old_num_val, 0, 
           (stat->num_val - old_num_val) * sizeof(*stat->p_valcnt));
    /* copy over the old stats in the existing size */
    memcpy(stat->p_valcnt, old_p_valcnt, 
           old_num_val * sizeof(*stat->p_valcnt));
    
    free(old_p_valcnt);                      /* free the old memory */
  }

  stat->p_valcnt[val] ++;

  stat->last_val = val;
  stat->last_is_null = 0;
}

void Report_NaturalNum_Update_Null(Report_NaturalNum_t * stat)
{
  stat->cnt ++;
  stat->null_cnt ++;

  stat->last_is_null = 1;
}

void Report_NaturalNum_Compute(const Report_NaturalNum_t * stat, 
                               int * p_num_val, double ** p_p_valcnt)
{
  int i;

  *p_num_val = stat->num_val;

  *p_p_valcnt = (double *) malloc(*p_num_val * sizeof(**p_p_valcnt));
  
  for (i=0; i<*p_num_val; i++)
    (*p_p_valcnt)[i] = ((double) stat->p_valcnt[i]) / ((double) stat->cnt);
}

void Report_NaturalNum_Print(const Report_NaturalNum_t * stat)
{
  int num_val;
  double * p_val_prob;
  int i;
  
  Report_NaturalNum_Compute(stat, &num_val, &p_val_prob);

  printf("{");
  
  printf("null: %.3g", ((double) stat->null_cnt)/ ((double) stat->cnt));
  
  for (i=0; i<num_val; i++)
  {
    if (p_val_prob[i])
      printf(", %d: %.3g", i, p_val_prob[i]);
  }

  printf("}\n");

  free(p_val_prob);
}

void Report_NaturalNum_Print_Last(const Report_NaturalNum_t * stat)
{
  printf("{");

  if (stat->last_is_null)
    printf("number: -1");
  else
    printf("number: %d", stat->last_val);
  
  printf("}\n");
}


/*****************************************************************************/

void Report_Finite_Init(Report_Finite_t * stat, int num_obj, ...)
{
  int i;
  va_list args;

  stat->cnt = 0;
  stat->null_cnt = 0;
  stat->num_obj = num_obj;
  stat->obj_cnt = (long *) calloc(num_obj, sizeof(*stat->obj_cnt));
  stat->obj_names = (char **) malloc(num_obj * sizeof(char *));
  
  va_start(args, num_obj);
  
  for (i=0; i<num_obj; i++)
  {
    stat->obj_names[i] = va_arg(args, char *);
  }
  
  va_end(args);

  stat->last_is_null = 1;
}

void Report_Finite_Update(Report_Finite_t * stat, int val)
{
  assert((val >=0) && (val < stat->num_obj));
  
  stat->obj_cnt[val] ++;
  stat->cnt ++;

  stat->last_val = val;
  stat->last_is_null = 0;
}

void Report_Finite_Update_Null(Report_Finite_t * stat)
{
  stat->cnt ++;
  stat->null_cnt ++;

  stat->last_is_null = 1;
}

void Report_Finite_Compute(const Report_Finite_t * stat, int * p_num_obj,
                           double ** pp_obj_prob)
{
  int i;
  double * p_obj_prob;
  
  p_obj_prob = (double *) malloc(sizeof(double) * stat->num_obj);

  for (i=0; i<stat->num_obj; i++)
  {
    p_obj_prob[i] = (double)stat->obj_cnt[i] 
      / ((double) stat->cnt);
  }
  *p_num_obj = stat->num_obj;
  *pp_obj_prob = p_obj_prob;
}

void Report_Finite_Print(const Report_Finite_t * stat)
{
  int num_obj;
  double * p_obj_prob;
  int i;
  
  Report_Finite_Compute(stat, &num_obj, &p_obj_prob);
  
  printf("{");

  printf("null: %.3g", ((double) stat->null_cnt)/stat->cnt);

  for (i=0; i<num_obj; i++)
  {
    printf(", %s: %.3g", stat->obj_names[i], p_obj_prob[i]);
  }

  printf("}\n");

  free(p_obj_prob);
}

void Report_Finite_Print_Last(const Report_Finite_t * stat)
{
  printf("{");

  if (stat->last_is_null)
    printf("object: -1");
  else
    printf("object: %d", stat->last_val);
  
  printf("}\n");
}

